package com.zui.security;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OrderBy;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import javax.persistence.Transient;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;
import org.hibernate.annotations.Cascade;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.GrantedAuthorityImpl;
import org.springframework.security.core.userdetails.UserDetails;

import com.sun.istack.internal.NotNull;
import com.zui.entity.BaseAutoIdEntity;
import com.zui.entity.party.Party;

/**
 * User entity.
 * 
 * 手写JPA annotation注明ORM关系，尽量使用JPA默认设定.
 * 用@Cache注明Hibernate缓存策略.
 * 用@OrderBy注明Set<Role> roles的排序，并用LinkedHashSet初始化roles. 
 * 
 * @author calvin
 */
@Entity
@Table(name = "user")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
@org.hibernate.annotations.Entity(
		selectBeforeUpdate = true,dynamicInsert = true, dynamicUpdate = true)
@NamedQueries({
	@NamedQuery(name="User.findByUserName",query="From User user where user.userName=?"),
	@NamedQuery(name="User.updatePassword",query="update User set password=? where userName=? and password=?")
})
//@NamedQueries({
//	@NamedQuery(name="User.findByUserName",query="From User user where user.userName=?"),
//	@NamedQuery(name="User.updatePassword",query="update User set password=? where userName=? and password=?"),
//	@NamedQuery(name="User.findByUserAndPass",query="From User user where user.userName=? and user.email=?")
//})
public class User  extends BaseAutoIdEntity implements UserDetails{
	private static final long serialVersionUID = 1L;
	
	private String userName;
	private String email;
	private String password;
	private String passwordHint;
	private String isSystem;
	private String isEnabled = FLAG_Y;
	private String hasLoggedOut;
	private String requirePasswordChange;
	private String lastCurrencyUom;
	private String lastLocale;
	private String preferedLanguage;
	private String localeCountry;
	private String lastTimeZone;
	private Date disabledDateTime;
	private Integer successiveFailedLogins;
	@Column(length=255)
	private String externalAuthId;
	@Column(length=255)
	private String userLdapDn;

	private Set<Role> roles = new LinkedHashSet<Role>();
	
	private Party party;
	private String signUpConfirmationCode;
	private String resetPasswordToken;

	
	/*------------------------------
	 * Form specific fields and Business Methods
	 --------------------------------*/	
	private String currentPassword;
	private String newPassword;
	private String confirmPassword;
	
	@Transient
	/**
	 * 
	 */
	public String getRolesName(){
		if(roles == null || roles.size() == 0)
			return "";
		String str = "";
		for(Iterator<Role> it = roles.iterator(); it.hasNext();){
			str += it.next().getRoleName();
			str += ",";
		}
		return str.substring(0,str.length()-1);
	}
	
	
	/*------------------------------
	 * UserDetails Interface methods
	 --------------------------------*/	
	@Override
	@Transient
	public Collection<GrantedAuthority> getAuthorities() {
		List<GrantedAuthority> authsList = new ArrayList<GrantedAuthority>();
		Set<Role> roles = this.getRoles();
		for (Role role : roles) {
			String[] auths = StringUtils.split(role.getAuthorities(), ",");
			for (String authName : auths) {
				authsList.add(new GrantedAuthorityImpl(authName));
			}
		}
		return authsList;
	}

	@Override
	@Transient
	public String getUsername() {
		return getUserName();
	}

	@Override
	@Transient
	public boolean isAccountNonExpired() {
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	@Transient
	public boolean isAccountNonLocked() {
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	@Transient
	public boolean isCredentialsNonExpired() {
		// TODO Auto-generated method stub
		return true;
	}

	@Override
	@Transient
	public boolean isEnabled() {
		boolean result = FLAG_Y.equals(getIsEnabled())? true: false;
		return result;
	}
	
	/*------------------------------
	 * Object methods
	 --------------------------------*/	
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof User)) return false;

        final User user = (User) o;

        if (userName != null ? !userName.equals(user.getUsername()) : user.getUsername() != null) return false;

        return true;
    }

    public int hashCode() {
        return (userName != null ? userName.hashCode() : 0);
    }

    public String toString() {
        ToStringBuilder sb = new ToStringBuilder(this,
                ToStringStyle.DEFAULT_STYLE).append("userName", this.userName)
                .append("enabled", this.isEnabled)
                .append("accountExpired",isAccountNonExpired())
                .append("credentialsExpired",isCredentialsNonExpired())
                .append("accountLocked",isAccountNonLocked());

        Collection<GrantedAuthority> auths = this.getAuthorities();
        if (auths != null) {
            sb.append("Granted Authorities: ");

            Iterator<GrantedAuthority> it = auths.iterator();
            boolean isFirst = true;
            while (it.hasNext()) {
            	if (isFirst)
            		isFirst = false;
            	else
            		sb.append(", ");
            	sb.append(it.next().toString());
            }
        } else {
            sb.append("No Granted Authorities");
        }
        return sb.toString();
    }
	/*------------------------------
	 * getters and annotations
	 --------------------------------*/	

	@Column(length=50,unique=true)
	@NotNull
	public String getUserName() {return userName;}
	
	@Column(length=50,unique=true)
	@NotNull
	public String getEmail() {return email;}
	
	@Column(length=50)
	@NotNull
	@Override
	public String getPassword() {return password;}

	@Column(length=50)
	public String getPasswordHint() {return passwordHint;}

	@Column(length=1)
	public String getIsSystem() {return isSystem;}
	
	@Column(length=1) @NotNull
	public String getIsEnabled() {return isEnabled;}
	
	@Column(length=1)
	public String getHasLoggedOut() {return hasLoggedOut;}

	@Column(length=1)
	public String getRequirePasswordChange() {return requirePasswordChange;}

	public String getLastCurrencyUom() {return lastCurrencyUom;}

	public String getLastLocale() {return lastLocale;}

	public String getPreferedLanguage() {return preferedLanguage;}

	public String getLocaleCountry() {return localeCountry;}

	public String getLastTimeZone() {
		return lastTimeZone;
	}
	
	@Temporal(TemporalType.TIME)
	public Date getDisabledDateTime() {
		return disabledDateTime;
	}

	public Integer getSuccessiveFailedLogins() {
		return successiveFailedLogins;
	}
	
	@Column(length=20)
	public String getExternalAuthId() {
		return externalAuthId;
	}

	public String getUserLdapDn() {
		return userLdapDn;
	}
	
	@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE},
			fetch = FetchType.LAZY)
	@JoinTable(name = "security_user_role", 
			joinColumns = { @JoinColumn(name = "userId",referencedColumnName = "id") }, 
			inverseJoinColumns = { @JoinColumn(name = "roleId",referencedColumnName = "id") })
	@OrderBy("roleName")
	public Set<Role> getRoles() {return roles;}
	
	@Transient
	public String getConfirmPassword() {return confirmPassword;}
	@Transient
	public String getCurrentPassword() {return currentPassword;}
	@Transient
	public String getNewPassword() {return newPassword;}
	@ManyToOne( cascade = {CascadeType.PERSIST, CascadeType.MERGE})
	@Cascade(org.hibernate.annotations.CascadeType.SAVE_UPDATE)
	@JoinColumn(name="partyId")
	public Party getParty() {return party;}
	@Column(length=100)
	public String getSignUpConfirmationCode() {return signUpConfirmationCode;}
	@Column(length=50)
	public String getResetPasswordToken() {return resetPasswordToken;}
	/*------------------------------
	 * setters
	 --------------------------------*/	
	
	public void setParty(Party party) {
		this.party = party;
	}

	public void setResetPasswordToken(String resetPasswordToken) {
		this.resetPasswordToken = resetPasswordToken;
	}

	public void setSignUpConfirmationCode(String signUpConfirmationCode) {
		this.signUpConfirmationCode = signUpConfirmationCode;
	}

	public void setEmail(String email) {
		this.email = email;
	}

	public void setUserName(String userName) {
		this.userName = userName;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public void setPasswordHint(String passwordHint) {
		this.passwordHint = passwordHint;
	}

	public void setIsSystem(String isSystem) {
		this.isSystem = isSystem;
	}

	public void setIsEnabled(String isEnabled) {
		this.isEnabled = isEnabled;
	}

	public void setHasLoggedOut(String hasLoggedOut) {
		this.hasLoggedOut = hasLoggedOut;
	}

	public void setRequirePasswordChange(String requirePasswordChange) {
		this.requirePasswordChange = requirePasswordChange;
	}

	public void setLastCurrencyUom(String lastCurrencyUom) {
		this.lastCurrencyUom = lastCurrencyUom;
	}

	public void setLastLocale(String lastLocale) {
		this.lastLocale = lastLocale;
	}

	public void setPreferedLanguage(String preferedLanguage) {
		this.preferedLanguage = preferedLanguage;
	}

	public void setLocaleCountry(String localeCountry) {
		this.localeCountry = localeCountry;
	}

	public void setLastTimeZone(String lastTimeZone) {
		this.lastTimeZone = lastTimeZone;
	}

	public void setDisabledDateTime(Date disabledDateTime) {
		this.disabledDateTime = disabledDateTime;
	}

	public void setSuccessiveFailedLogins(Integer successiveFailedLogins) {
		this.successiveFailedLogins = successiveFailedLogins;
	}

	public void setExternalAuthId(String externalAuthId) {
		this.externalAuthId = externalAuthId;
	}

	public void setUserLdapDn(String userLdapDn) {
		this.userLdapDn = userLdapDn;
	}

	public void setRoles(Set<Role> roles) {
		this.roles = roles;
	}

	public void setConfirmPassword(String confirmPassword) {
		this.confirmPassword = confirmPassword;
	}
	public void setCurrentPassword(String currentPassword) {
		this.currentPassword = currentPassword;
	}
	public void setNewPassword(String newPassword) {
		this.newPassword = newPassword;
	}
}